home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 November: Tool Chest / Dev.CD Nov 98 TC.toast / Sample Code / Networking / OT Server Sample / OTVirtualServer / OTVirtualServer.c next >
Encoding:
Text File  |  1996-12-19  |  61.9 KB  |  2,220 lines  |  [TEXT/CWIE]

  1. //
  2. //        OTVirtualServer             by Eric Okholm            Version 1.0.1
  3. //
  4. //        This is an OpenTransport sample server application which demonstrates
  5. //        a fast framework for making an OpenTransport server application.
  6. //
  7. //        This version of the server simply opens a listener endpoint and
  8. //        many endpoints which can accept connections.  When inbound connections
  9. //        are received, it waits to receive a 128 byte "request", then it sends 
  10. //        a predetermined data from memory (not disk) and begins an orderly release 
  11. //        of the connection.
  12. //
  13. //        Future iterations of this program will retreive data from disk to return,
  14. //        demonstrating synchronization methods, and do ADSP, demonstrating 
  15. //        protocol independence.
  16. //
  17. //        You are welcome to use this code in any way to create you own
  18. //        OpenTransport applications.   For more information on this program,
  19. //        please review the document "About OTVirtual Server".
  20. //
  21. //        Go Bears, beat Stanford !!!
  22. //
  23. //        What's new in version 1.0.1:
  24. //
  25. //        (1) Worked around a bug found when using AckSends and sending the same
  26. //            buffer more than once.   See the routine SendData for details.
  27. //
  28. //        To do:
  29. //
  30. //        (1) Improve statistics window.
  31. //        (2) General routine for processing kOTLookErrs
  32. //        (3) Handle inbound T_ORDREL processing inside other notifications.
  33. //        (4) Allow running on OT 1.1 by including a copy of tilisten module to install.
  34. //
  35.  
  36. #define DoAlert(x)            { sprintf(gProgramErr, x); gProgramState = kProgramError; }        
  37. #define DoAlert1(x, y)        { sprintf(gProgramErr, x, y); gProgramState = kProgramError; }        
  38. #define DoAlert2(x, y, z)    { sprintf(gProgramErr, x, y, z); gProgramState = kProgramError;}    
  39.  
  40. //
  41. //    Program mode
  42. //
  43. //    Before compiling, 
  44. //    set kDebugLevel to 0 for production
  45. //                    or 1 for debug code.
  46. //
  47. //    In production mode, the code attempts to recover cleanly from any problems in encounters.
  48. //    In debug mode, the unexplained phenomenon cause an alert box highlighting the situation
  49. //    to be delivered and then the program exits.
  50. //
  51.  
  52. #define kDebugLevel    1
  53.  
  54. #if kDebugLevel > 0
  55.  
  56. #define DBAlert(x)            DoAlert(x)        
  57. #define DBAlert1(x, y)        DoAlert1(x, y)        
  58. #define DBAlert2(x, y, z)    DoAlert2(x, y, z)    
  59.  
  60. #else
  61.  
  62. #define DBAlert(x)            { }
  63. #define DBAlert1(x, y)        { }
  64. #define DBAlert2(x, y, z)    { }
  65.  
  66. #endif
  67.  
  68. //
  69. //    Include files
  70. //
  71. #include <Dialogs.h>
  72. #include <Events.h>
  73. #include <Fonts.h>
  74. #include <GestaltEqu.h>
  75. #include <Memory.h>
  76. #include <Menus.h>
  77. #include <QuickDraw.h>
  78. #include <SegLoad.h>
  79. #include <stdio.h>
  80. #include <StdLib.h>
  81. #include <String.h>
  82. #include <strings.h>
  83. #include <ToolUtils.h>
  84. #include <Windows.h>
  85.  
  86. #include <OpenTptInternet.h>        // includes OpenTransport.h
  87. #include <OpenTptClient.h>            // needed for OTReleaseBuffer()    
  88.  
  89. //
  90. //    Defines, enums, resource IDs
  91. //
  92. #define kInFront                    (WindowPtr) -1
  93. #define    kWindowResID                128
  94.  
  95.     // Apple Menu
  96. #define kAppleMenuResID                128
  97. #define    kAppleMenuAbout                1
  98.  
  99.     // File Menu
  100. #define kFileMenuResID                129
  101. #define kFileMenuOpen                1
  102. #define    kFileMenuClose                2
  103. #define    kFileMenuQuit                4
  104.  
  105.     // Edit Menu
  106. #define kEditMenuResID                130                // Edit menu is disabled
  107.  
  108.     // Server Menu
  109. #define kServerMenuResID            131
  110. #define kServerMenuTCPPrefs            1
  111.  
  112.     // Alerts, etc.
  113. #define kAlertExitResID                128
  114. #define kAboutBoxResID                130
  115.  
  116.     // TCP Prefs Dialog
  117. #define kTCPPrefsDlogResID            129
  118. #define    kListenerPortDItem            2
  119. #define    kListenerQueueDepthDItem    4
  120. #define    kMaxConnectionsDItem        6
  121. #define    kReturnDataLengthDItem        8
  122. #define kStartStopDItem                9
  123.  
  124.     // Overall program states
  125. enum
  126. {
  127.     kProgramRunning        = 0,
  128.     kProgramDone        = 1,
  129.     kProgramError        = 2
  130. };
  131.  
  132.     // Server states
  133. enum
  134. {
  135.     kServerStopped        = 0,
  136.     kServerRunning        = 1,
  137.     kServerShuttingDown    = 2
  138. };
  139.  
  140.     // Bit numbers in EPInfo stateFlags fields
  141. enum
  142. {
  143.     kBrokenBit                        = 0,
  144.     kOpenInProgressBit                = 1,
  145.     kFlushDisconnectInProgressBit    = 2,
  146.     kIPReuseAddrBit                    = 3,
  147.     kPassconBit                        = 4,
  148.     kGotRequestBit                    = 5,
  149.     kWaitingBit                        = 6
  150. };
  151.  
  152.     // Misc stuff
  153. enum
  154. {
  155.     kDontQueueIt                = 0,
  156.     kQueueIt                    = 1,
  157.     kTimerHitsBeforeAcceptMax    = 2,
  158.     kTimerIntervalInSeconds        = 3,
  159.     kTimerInterval                = (kTimerIntervalInSeconds * 1000),
  160.     kRequestSize                = 128,
  161.     kOTVersion113                = 0x01130000,
  162.     kOTVersion111                = 0x01110000,
  163.     kDataBufSize                = (16 * 1024),
  164.     kTCPKeepAliveInSecs            = (30 * 1000)        // 30 sec in msec.
  165. };
  166.  
  167.  
  168. //
  169. //    Globals
  170. //
  171. int                    gServerState                = kServerStopped;
  172. int                    gProgramState                = kProgramRunning;
  173. char                gProgramErr[128];
  174. DialogPtr            gDialogPtr                    = NULL;
  175. WindowPtr            gWindowPtr                    = NULL;
  176. long                gSleepTicks                    = 60;
  177. Str255                gListenerPortStr            = "\p2001";
  178. long                gListenerPort                = 2001;
  179. Str255                gListenerQueueDepthStr        = "\p20";
  180. long                gListenerQueueDepth            = 20;
  181. Str255                gMaxConnectionsStr            = "\p100";
  182. long                gMaxConnections                = 100;
  183. long                gMaxConnectionsAllowed        = 0;
  184. Str255                gReturnDataLengthStr        = "\p2048";
  185. long                gReturnDataLength            = 2048;
  186. Boolean                gServerRunning                = false;
  187. Str255                gStartStr                    = "\pStart";
  188. Str255                gStopStr                    = "\pStop";
  189. SInt32                gCntrEndpts                    = 0;
  190. SInt32                gCntrIdleEPs                = 0;
  191. SInt32                gCntrBrokenEPs                = 0;
  192. SInt32                gCntrConnections            = 0;
  193. SInt32                gCntrTotalBrokenEPs            = 0;
  194. SInt32                gCntrTotalConnections        = 0;
  195. SInt32                gCntrTotalBytesSent            = 0;
  196. Boolean                gListenPending                = false;
  197. OTLIFO                gIdleEPLIFO;
  198. OTLIFO*                gIdleEPs                    = &gIdleEPLIFO;
  199. OTLIFO                gBrokenEPLIFO;
  200. OTLIFO*                gBrokenEPs                    = &gBrokenEPLIFO;
  201. OTLIFO                gWaitingEPLIFO;
  202. OTLIFO*                gWaitingEPs                    = &gWaitingEPLIFO;
  203. OTConfiguration*    gCfgMaster                    = NULL;
  204. long                gTimerTask                    = 0;
  205. SInt32                gCntrIntervalConnects        = 0;
  206. SInt32                gCntrIntervalBytes            = 0;
  207. SInt32                gConnectsPerSecond            = 0;
  208. SInt32                gConnectsPerSecondMax        = 0;
  209. SInt32                gKBytesPerSecond            = 0;
  210. SInt32                gKBytesPerSecondMax            = 0;
  211. SInt32                gCntrIntervalEventLoop        = 0;
  212. SInt32                gEventsPerSecond            = 1;
  213. SInt32                gEventsPerSecondMax            = 1;
  214. Boolean                gWaitForEventLoop            = false;
  215. Boolean                gDoWindowUpdate                = true;
  216. SInt32                gAllowNewMax                = kTimerHitsBeforeAcceptMax;
  217. OSType                gOTVersionSelector            = 'otvr';
  218. UInt32                gOTVersion;
  219.  
  220. struct EPInfo
  221. {
  222.     EndpointRef        erf;                //    actual endpoint
  223.     OTLink            link;                //    link into an OT LIFO (atomic)
  224.     SInt32            outstandingSends;    //    number of T_MEMORYRELEASED events expected
  225.     unsigned char*    sendPtr;            //    ptr to next byte to send
  226.     UInt32            sendBytes;            //    remaining bytes to send
  227.     SInt32            rcvdBytes;            //    bytes received (we pretend 128 bytes in is a request for download)
  228.     UInt8            stateFlags;            //    various status fields
  229. };
  230. typedef struct EPInfo EPInfo;
  231.  
  232. EPInfo*    gListener    = NULL;
  233. EPInfo* gAcceptors    = NULL;
  234.  
  235. unsigned char gDataBuf[kDataBufSize];
  236.  
  237. //
  238. //    Option structure 
  239. //    
  240. //    This is used to pass down both IP_REUSEADDR and TCP_KEEPALIVE in the
  241. //    same option message
  242. //
  243.  
  244. struct TKeepAliveOpt
  245. {
  246.     UInt32        len;
  247.     OTXTILevel    level;
  248.     OTXTIName    name;
  249.     UInt32        status;
  250.     UInt32        tcpKeepAliveOn;
  251.     UInt32        tcpKeepAliveTimer;
  252. };
  253. typedef struct TKeepAliveOpt TKeepAliveOpt;
  254.  
  255. //
  256. //    OpenTransport Networking Code Prototypes
  257. //
  258. static void            CheckUnbind(EPInfo*, OTResult, Boolean);
  259. static void            DoListenAccept();
  260. static void            DoRcvDisconnect(EPInfo*);
  261. static void            EnterListenAccept();
  262. static Boolean         EPClose(EPInfo*);
  263. static Boolean        EPOpen(EPInfo*);
  264. static void            NetInit(void);
  265. static void            NetShutdown(void);
  266. static pascal void    Notifier(void*, OTEventCode, OTResult, void*);
  267. static void            ReadData(EPInfo*);
  268. static void            Recycle(void);
  269. static void            SendData(EPInfo*);
  270. static void         StartServer(void);
  271. static void         StopServer(void);
  272. static void            TimerInit();
  273. static void            TimerDestroy();
  274. static pascal void    TimerRun(void*);
  275.  
  276. //
  277. //    Macintosh Program Wrapper Prototypes 
  278. //
  279. static void            AboutBox(void);
  280. static void            AlertExit(Str255);
  281. static void         C2PStr(char*, Str255);
  282. static void            DialogClose(void);
  283. static Boolean         EventDialog(EventRecord*);
  284. static void            EventDrag(WindowPtr, Point);
  285. static void            EventGoAway(WindowPtr, Point);
  286. static void            EventKeyDown(EventRecord*);
  287. static void            EventLoop(void);
  288. static void            EventMouseDown(EventRecord*);
  289. static void         MacInit(void);
  290. static void         MacInitROM(void);
  291. static void         MenuDispatch(long);
  292. static void         P2CStr(Str255, char*);
  293. static void         SetupMenus(void);
  294. static void         TCPPrefsDialog(void);
  295. static void            TCPPrefsReset(void);
  296. static void         WindowClose(void);
  297. static void         WindowOpen(void);
  298. static void            WindowUpdate(void);
  299.  
  300.  
  301. //////////////////////////////////////////////////////////////////////////////////////
  302. //
  303. //    OpenTransport Networking Code
  304. //
  305. //    The code in this section provides the networking portions of the 
  306. //    OpenTransport Virtual Server.
  307. //
  308. //////////////////////////////////////////////////////////////////////////////////////
  309.  
  310. //
  311. //    CheckUnbind
  312. //
  313. //    This routine checks the results of an unbind.   Due to various problems
  314. //    in OpenTransport, an OTUnbind can fail for a number of reasons.  This problem
  315. //    is timing related so you usually won't hit it.   When an OTUnbind fails,
  316. //    we assume the best way to recover is to throw the endpoint on the broken
  317. //    list to be recycled.   Later, in the recycle routine, it will be closed
  318. //    and a new endpoint will be opened to replace it.  If the OTUnbind is
  319. //    successful, the endpoint is put back on the free list to be reused.
  320. //
  321. //    Since the unbind failure is timing related, a more efficient solution
  322. //    would probably be to wait and retry the unbind in a few seconds, 
  323. //    expecting that the call would not fail on the next try.
  324. //
  325. static void CheckUnbind(EPInfo* epi, OTResult result, Boolean queueIt)
  326. {
  327.     if (result != kOTNoError)
  328.     {
  329.         if ( OTAtomicSetBit(&epi->stateFlags, kBrokenBit) == 0 )
  330.         {
  331.             //
  332.             //    The OTAtomicSetBit guarantee's that the EPInfo won't be
  333.             //    enqueued twice.   We only enqueue the EPInfo if the previous
  334.             //    state of the bit was 0.
  335.             //
  336.             OTLIFOEnqueue(gBrokenEPs, &epi->link);
  337.             OTAtomicAdd32(1, &gCntrBrokenEPs);
  338.             OTAtomicAdd32(1, &gCntrTotalBrokenEPs);
  339.         }
  340.     }
  341.     else
  342.     {
  343.         if (queueIt)
  344.         {
  345.             OTLIFOEnqueue(gIdleEPs, &epi->link);
  346.             OTAtomicAdd32(1, &gCntrIdleEPs);
  347.             if (gListenPending)
  348.                 EnterListenAccept();
  349.         }
  350.     }
  351. }
  352.  
  353. //
  354. //    EnterListenAccept
  355. //
  356. //    This is a front end to DoListenAccept() which is used whenever 
  357. //    it is not being called from inside the listener endpoint's notifier.
  358. //    We do this for syncrhonization.   If we were processing an OTListen()
  359. //    or an OTAccept() and we were interrupted at the listener endpoint's
  360. //    notifier with a T_LISTEN, etc, it would be inconvenient and would require
  361. //    some more sophisticated synchronization code to deal with the problem.
  362. //    The easy way to avoid this is to do an OTEnterNotifier() on the listener's
  363. //    endpoint.   
  364. //
  365. //    Important note - doing OTEnterNotifier on one endpoint only prevents that
  366. //    endpoint's notifier for interrupting us.   Since the same notifier code
  367. //    is used for lots of endpoints here, remember that the one endpoint's 
  368. //    notifier can interrupt another.   Doing an OTEnterNotifier() on the
  369. //    listener endpoint prevents the listener from interrupting us, but it
  370. //    does not prevent the Notifier() routine from interrupting us via 
  371. //    another endpoint which also uses the same routine.
  372. //
  373. //    Important note #2 - Don't ever do an OTEnterNotifier on an acceptor endpoint
  374. //    before doing the OTAccept().   This confuses OT and creates problems.
  375. //
  376. static void EnterListenAccept()
  377. {
  378.     Boolean    doLeave;
  379.     
  380.     doLeave = OTEnterNotifier(gListener->erf);
  381.     DoListenAccept();
  382.     if (doLeave)
  383.         OTLeaveNotifier(gListener->erf);
  384. }
  385.  
  386. //
  387. //    DoListenAccept
  388. //
  389. //    The handling of a T_LISTEN is greatly simplified by use
  390. //    of the tilisten module, which serializes inbound connections.
  391. //    This means that when doing an OTAccept we won't get a kOTLookErr
  392. //    because another inbound connection arrived and created a T_LISTEN.
  393. //    Without the tilisten module, we have to use the "8 step 
  394. //    listen/accept/disconnect method", which is documented elsewhere.
  395. //    At this point, if we have a free endpoint, accept the connection.
  396. //    If we don't, assume we are overloaded and reject the connection.
  397. //
  398. //    When we are called from inside the notifier due to a T_LISTEN, 
  399. //    DoListenAccept() is called directly.
  400. //
  401. //    When we restart delayed handling of a T_LISTEN, either because of
  402. //    doing a throttle-back or because the program ran out of free endpoints,
  403. //    EnterListenAccept() is called for synchronization on the listener endpoint.
  404. //
  405. static void DoListenAccept()
  406. {
  407.     TCall        call;
  408.     InetAddress    caddr;
  409.     OTResult    lookResult;
  410.     OTLink*        acceptor_link;
  411.     EPInfo*        acceptor;
  412.     OSStatus    err;
  413.     
  414.     //
  415.     //    By deferring handling of a T_LISTEN, we can slow down inbound requests
  416.     //    and get some time to make sure the event loop occurs.   This is important
  417.     //    so that: (1) the user can quit the program, (2) so memory can be restructured,
  418.     //    (3) so we can recycle broken endpoints and other administrative tasks that
  419.     //    are not done in the notifier.
  420.     //
  421.     if (gWaitForEventLoop)
  422.     {
  423.         gListenPending = true;
  424.         return;
  425.     }
  426.     
  427.     //
  428.     //    Get an EPInfo & endpoint.   If none are available, defer handling the T_LISTEN.
  429.     //
  430.     acceptor_link = OTLIFODequeue(gIdleEPs);
  431.     if (acceptor_link == NULL)
  432.     {
  433.         gListenPending = true;
  434.         return;
  435.     }
  436.     
  437.     OTAtomicAdd32(-1, &gCntrIdleEPs);
  438.     gListenPending = false;
  439.     acceptor = OTGetLinkObject(acceptor_link, EPInfo, link);
  440.     acceptor->stateFlags = 0;
  441.     acceptor->rcvdBytes = 0;
  442.         
  443.     call.addr.maxlen = sizeof(InetAddress);
  444.     call.addr.buf = (unsigned char*) &caddr;
  445.     call.opt.maxlen = 0;
  446.     call.opt.buf = NULL;
  447.     call.udata.maxlen = 0;
  448.     call.udata.buf = NULL;
  449.         
  450.     err = OTListen(gListener->erf, &call);
  451.     if (err != kOTNoError)
  452.     {
  453.         //
  454.         //    Only two errors are expected at this point.
  455.         //    One would be a kOTNoDataErr, indicating the inbound connection
  456.         //    was unavailable, temporarily hidden by a higher priority streams
  457.         //    message, etc.   The more likely error is a kOTLookErr, 
  458.         //    which indicates a T_DISCONNECT on the OTLook()
  459.         //    happens when the call we were going to process disconnected.
  460.         //    In that case, go away and wait for the next T_LISTEN event.
  461.         //
  462.         OTLIFOEnqueue(gIdleEPs, &acceptor->link);
  463.         OTAtomicAdd32(1, &gCntrIdleEPs);
  464.         if (err == kOTNoDataErr)
  465.             return;
  466.             
  467.         lookResult = OTLook(gListener->erf);
  468.         if (err == kOTLookErr && lookResult == T_DISCONNECT)
  469.             DoRcvDisconnect(gListener);
  470.         else    
  471.             DBAlert2("Notifier: T_LISTEN - OTListen error %d lookResult %x", err, lookResult);
  472.         return;    
  473.     }
  474.     
  475.     err = OTAccept(gListener->erf, acceptor->erf, &call);
  476.     if (err != kOTNoError)
  477.     {
  478.         //
  479.         //    Again, we have to be able to handle the connection being disconnected
  480.         //    while we were trying to accept it.
  481.         //
  482.         OTLIFOEnqueue(gIdleEPs, &acceptor->link);
  483.         OTAtomicAdd32(1, &gCntrIdleEPs);
  484.         lookResult = OTLook(gListener->erf);
  485.         if (err == kOTLookErr && lookResult == T_DISCONNECT)
  486.             DoRcvDisconnect(gListener);
  487.         else
  488.             DBAlert2("Notifier: T_LISTEN - OTAccept error %d lookResult %x", err, lookResult);
  489.     }
  490. }
  491.  
  492.  
  493. //
  494. //    DoRcvDisconnect
  495. //
  496. //    This routine is called from the notifier in T_LISTEN handling
  497. //    upon getting a kOTLookErr back indicating a T_DISCONNECT needs to be handled.
  498. //
  499. static void DoRcvDisconnect(EPInfo* epi)
  500. {
  501.     OSStatus err;
  502.     
  503.     err = OTRcvDisconnect(epi->erf, NULL);
  504.     if (epi == gListener)
  505.     {
  506.         //
  507.         //    We can get a disconnect on the listener if an inbound connection was
  508.         //    being disconnected (sent a RST) while we were in the process of refusing
  509.         //    it because we had no idle endpoints).   In this case, we don't really
  510.         //    want to do anything other than receive the disconnect and move on.
  511.         //
  512.         if (err != kOTNoError)
  513.             DBAlert1("DoRcvDisconnect: OTRcvDisconnect on listener error %d", err);
  514.         return;
  515.     }
  516.     if (err != kOTNoError)
  517.     {
  518.         if (err != kOTNoDisconnectErr)
  519.             DBAlert1("DoRcvDisconnect: OTRcvDisconnect error %d", err);
  520.         return;
  521.     }
  522.     
  523.     //
  524.     //    Don't start the unbind yet if the endpoint is on the waiting list
  525.     //    and is scheduled for an orderly release (which can no longer happen).
  526.     //    Instead, if it is scheduled, just clear the bit so we know later
  527.     //    to do the unbind instead of the orderly relase.
  528.     //
  529.     if ((OTAtomicClearBit(&epi->stateFlags, kWaitingBit)) == 0)
  530.         CheckUnbind(epi, OTUnbind(epi->erf), kDontQueueIt);
  531. }
  532.  
  533.  
  534. //
  535. //    DoSndOrderlyDisconnect
  536. //
  537. //    This routine is a front end to OTSndOrderlyDisconnect().
  538. //    In OT 1.1.2 and earlier releases, there is a problem in OT/TCP which can cause
  539. //    OT/TCP to forget to send the orderly release indication upstream if the system
  540. //    is running so fast the event loop doesn't get time.  To work around this problem,
  541. //    we defer sending the orderly release until the event loop runs.   In OT 1.1.3 and
  542. //    later the routine is called from the notifier instead.  The cost of this workaround
  543. //    is about 18% in terms of connections per second, but the workaround appears to
  544. //    be 100% reliable.
  545. //
  546. static void DoSndOrderlyDisconnect(EPInfo* epi)
  547. {
  548.     OSStatus err;
  549.     OTResult epState;
  550.     
  551.     err = OTSndOrderlyDisconnect(epi->erf);
  552.     epState = OTGetEndpointState(epi->erf);
  553.     if (err != kOTNoError)
  554.     {
  555.         DBAlert2("DoSndOrderlyDisconnect: OTSndOrderlyDisconnect error %d state %d", err, epState);
  556.         return;
  557.     }
  558.  
  559.     //
  560.     //    Check the endpoint state to see if we are in T_IDLE.  If so,
  561.     //    the connection is fully broken down and we can unbind are requeue
  562.     //    the endpoint for reuse.   If not, then wait until we have also received
  563.     //    an orderly release from the other side, at which time we will also check 
  564.     //    the state of the endpoint and unbind there if required.
  565.     //
  566.     epState = OTGetEndpointState(epi->erf);
  567.     if (epState == T_IDLE)
  568.     {
  569.         CheckUnbind(epi, OTUnbind(epi->erf), kDontQueueIt);
  570.     }
  571. }
  572.  
  573. //
  574. //    DoWaitList
  575. //
  576. //    This routine is only used when running on OT 1.1.2 or earlier releases.
  577. //    Check the comments in DoSndOrderlyDisconnect for an explanation.
  578. //    We always check the kWaitingBit to make sure we still need to do the
  579. //    orderly release.   If it has been cleared, then the endpoint has already
  580. //    been disconnected and we can just toss it back into the idle list.
  581. //
  582. static void DoWaitList()
  583. {
  584.     OTLink*     list = OTLIFOStealList(gWaitingEPs);
  585.     OTLink*        link;
  586.     EPInfo*        epi;
  587.  
  588.     while ( (link = list) != NULL )
  589.     {
  590.         list = link->fNext;
  591.         epi = OTGetLinkObject(link, EPInfo, link);
  592.         if ((OTAtomicClearBit(&epi->stateFlags, kWaitingBit)) != 0)
  593.             DoSndOrderlyDisconnect(epi);
  594.         else
  595.             CheckUnbind(epi, OTUnbind(epi->erf), kDontQueueIt);
  596.     }
  597. }
  598.  
  599.  
  600. //
  601. //    EPClose
  602. //
  603. //    This routine is a front end to OTCloseProvider.   Centralizing closing of
  604. //    endpoints makes debugging and instrumentation easier.   Also, since this
  605. //    program uses Ack Sends to avoid data copies when doing OTSnd(), some special
  606. //    care is required at close time.   
  607. //
  608. static Boolean EPClose(EPInfo* epi)
  609. {
  610.     OSStatus err;
  611.     
  612.     //
  613.     //    If an endpoint is still being opened, we can't close it yet.
  614.     //    There is no way to cancel an OTAsyncOpenEndpoint, so we just
  615.     //    have to wait for the T_OPENCOMPLETE event at the notifier.
  616.     //
  617.     if (OTAtomicTestBit(&epi->stateFlags, kOpenInProgressBit) != 0)
  618.         return false;
  619.     
  620.     //
  621.     //    If the OTAsyncOpenEndpoint failed, the endpoint ref will be NULL,
  622.     //    and we don't need to close it now.
  623.     //
  624.     if (epi->erf == NULL)
  625.         return true;
  626.         
  627.     if (epi->outstandingSends == 0)
  628.     {
  629.         err = OTCloseProvider(epi->erf);
  630.         epi->erf = NULL;
  631.         if (err != kOTNoError)
  632.             DBAlert1("EPClose: OTCloseProvider error %d", err);
  633.         if (epi != gListener)
  634.             gCntrEndpts--;
  635.         return true;
  636.     }
  637.     
  638.     //
  639.     //    If we get to this point, the endpoint did an OTSnd() with AckSends,
  640.     //    and the T_MEMORYRELEASED event hasn't been returned yet.  In order
  641.     //    to make sure we get the event, we flush the stream and then do an
  642.     //    OTDisconnect().   This should get the memory freed so we can close
  643.     //    the endpoint safely.   Note, we set a flag so we don't do this 
  644.     //    more than once on an endpoint.
  645.     //
  646.     if ( OTAtomicSetBit(&epi->stateFlags, kFlushDisconnectInProgressBit) == 0 )
  647.     {
  648.         err = OTIoctl(epi->erf, I_FLUSH, (void *)FLUSHRW);
  649.         if (err != kOTNoError)
  650.             DBAlert1("EPClose: I_FLUSH error %d", err);
  651.     }
  652.     return false;
  653. }
  654.  
  655. //
  656. //    EPOpen:
  657. //
  658. //    A front end to OTAsyncOpenEndpoint.
  659. //    A status bit is set so we know there is an open in progress.
  660. //    It is cleared when the notifier gets a T_OPENCOMPLETE where the context
  661. //    pointer is this EPInfo.  Until that happens, this EPInfo can't be cleaned
  662. //    up and released.
  663. //
  664. static Boolean EPOpen(EPInfo* epi, OTConfiguration* cfg)
  665. {
  666.     OSStatus err;
  667.     
  668.     //
  669.     //    Clear all old state bits and set the open in progress bit.
  670.     //    This doesn't need to be done atomicly because we are 
  671.     //    single threaded on this endpoint at this point.
  672.     //
  673.     epi->erf = NULL;
  674.     epi->stateFlags = 1 << kOpenInProgressBit;        
  675.     err = OTAsyncOpenEndpoint(cfg, 0, NULL, &Notifier, epi);
  676.     if (err != kOTNoError)
  677.     {
  678.         OTAtomicClearBit(&epi->stateFlags, kOpenInProgressBit);
  679.         DBAlert1("EPOpen: OTAsyncOpenEndpoint error %d", err);
  680.         return false;
  681.     }
  682.     return true;
  683. }
  684.  
  685. //
  686. //    NetEventLoop
  687. //
  688. //    This routine is called once during each pass through the program's event loop.
  689. //    If the program is running on OT 1.1.2 or an earlier release, this is where
  690. //    outbound orderly releases are started (see comments in DoSndOrderlyRelease 
  691. //    for more information on that).   This is also where endpoints are "fixed" by
  692. //    closing them and opening a new one to replace them.   This is rarely necessary,
  693. //    but works around some timing issues in OTUnbind().  Having passed through the 
  694. //    event loop once, we assume it is safe to turn off throttle-back.  And, finally,
  695. //    if we have deferred handing of a T_LISTEN, here we start it up again.
  696. //
  697. static void NetEventLoop()
  698. {
  699.     if (gOTVersion < kOTVersion113)
  700.         DoWaitList();
  701.     Recycle();
  702.     gWaitForEventLoop = false;
  703.     if (gListenPending)
  704.         EnterListenAccept();
  705. }
  706.  
  707. //
  708. //    NetInit:
  709. //
  710. //    This routine does various networking related startup tasks:
  711. //
  712. //    (1) it does InitOpenTransport
  713. //    (2) it records the OT version for us.
  714. //    (3) it starts our timer interrupt running.
  715. //
  716. static void NetInit()
  717. {
  718.     OSStatus err;
  719.     
  720.     err = InitOpenTransport();
  721.     if (err)
  722.     {
  723.         DBAlert1("NetInit: InitOpenTransport error %d", err);
  724.         return;
  725.     }
  726.     err = Gestalt(gOTVersionSelector, (long*) &gOTVersion);
  727.     if (err || (gOTVersion < kOTVersion111))
  728.     {
  729.         DoAlert("Please install Open Transport 1.1.1 or later");
  730.         return;
  731.     }
  732.     TimerInit();
  733. }
  734.  
  735. //
  736. //    NetShutdown:
  737. //
  738. //    This routine does various networking related shutdown tasks:
  739. //
  740. static void NetShutdown()
  741. {
  742.     TimerDestroy();
  743.     CloseOpenTransport();
  744. }    
  745.  
  746. //
  747. //    Notifier:
  748. //
  749. //    Most of the interesting networking code in this program resides inside 
  750. //    this notifier.   In order to run asynchronously and as fast as possible,
  751. //    things are done inside the notifier whenever possible.  Since almost
  752. //    everything is done inside the notifier, there was little need for specical
  753. //    synchronization code.
  754. //
  755. //    In the next iteration of this program, when information to be sent is 
  756. //    actually retreived from the disk, the synchronization, particularly for
  757. //    doing sends and handling flow control, will become more complicated. 
  758. //
  759. //    IMPORTANT NOTE:  Normal events defined by XTI (T_LISTEN, T_CONNECT, etc)
  760. //    and OT completion events (T_OPENCOMPLETE, T_BINDCOMPLETE, etc.) are not
  761. //    reentrant.  That is, whenever our notifier is invoked with such an event,
  762. //    the notifier will not be called again by OT for another normal or completion
  763. //    event until we have returned out of the notifier - even if we make OT calls
  764. //    from inside the notifier.   This is a useful synchronization tool.
  765. //    However, there are two kinds of events which will cause the notifier to 
  766. //    be reentered.   One is T_MEMORYRELEASED, which always happens instantly.
  767. //    The other are state change events like kOTProviderWillClose.
  768. //
  769. static pascal void Notifier(void* context, OTEventCode event, OTResult result, void* cookie)
  770. {
  771.     OSStatus err;
  772.     OTResult epState;
  773.     EPInfo* epi = (EPInfo*) context;
  774.  
  775.     //
  776.     //    Once the program is shutting down, most events would be uninteresting.
  777.     //    However, we still need T_OPENCOMPLETE and T_MEMORYRELEASED events since
  778.     //    we can't call CloseOpenTransport until all OTAsyncOpenEndpoints and
  779.     //    OTSends with AckSends have completed.   So those specific events
  780.     //    are still accepted.
  781.     //
  782.     if (gProgramState != kProgramRunning)
  783.     {
  784.         if ((event != T_OPENCOMPLETE) && (event != T_MEMORYRELEASED))
  785.         {
  786.             return;
  787.         }
  788.     }
  789.  
  790.     //
  791.     //    This really isn't necessary, it's just a sanity check which should be removed
  792.     //    once a program is debugged.   It's just making sure we don't get event notifications
  793.     //    after all of our endpoints have been closed.
  794.     //
  795.     if (gServerState == kServerStopped)
  796.     {
  797.         DBAlert1("Notifier: got event %d when server not running!", event);
  798.         return;
  799.     }
  800.     
  801.     //
  802.     //    Within the notifier, all action is based on the event code.
  803.     //    In this notifier, fatal errors all break out of the switch to the bottom.
  804.     //    As long as everything goes as expected, the case returns rather than breaks.
  805.     //
  806.     switch (event)
  807.     {
  808.         //
  809.         //    kStreamIoctlEvent:
  810.         //
  811.         //    This event is returned when an I_FLUSH ioctl has completed.
  812.         //    The flush was done in an attempt to get back all T_MEMORYRELEASED events
  813.         //    for outstanding OTSnd() calls with Ack Sends.   For good measure, we
  814.         //    send a disconnect now.   Errors are ignored at this point since it is
  815.         //    possible that the connection will already be gone, etc.
  816.         //
  817.         case kStreamIoctlEvent:
  818.         {
  819.             if (OTAtomicTestBit(&epi->stateFlags, kOpenInProgressBit) != 0)
  820.                 (void) OTSndDisconnect(epi->erf, NULL);
  821.             return;
  822.         }
  823.         
  824.         //
  825.         //    T_ACCEPTCOMPLETE:
  826.         //
  827.         //    This event is received by the listener endpoint only.   
  828.         //    The acceptor endpoint will get a T_PASSCON event instead.
  829.         //
  830.         case T_ACCEPTCOMPLETE:
  831.         {
  832.             if (result != kOTNoError)
  833.                 DBAlert1("Notifier: T_ACCEPTCOMPLETE - result %d", result);
  834.             return;
  835.         }
  836.         
  837.         //
  838.         //    T_BINDCOMPLETE:
  839.         //
  840.         //    We only bind the listener endpoint, and bind failure is a fatal error.  
  841.         //    Acceptor endpoints are bound within the OTAccept() call when they get a connection.
  842.         //
  843.         case T_BINDCOMPLETE:
  844.         {
  845.             if (result != kOTNoError)
  846.                 DoAlert("Unable to set up listening endpoint, exiting");
  847.             return;
  848.         }
  849.         
  850.         //
  851.         //    T_DATA:
  852.         //
  853.         //    The main rule for processing T_DATA's is to remember that once you have
  854.         //    a T_DATA, you won't get another one until you have read to a kOTNoDataErr.
  855.         //    The advanced rule is to remember that you could get another T_DATA
  856.         //    during an OTRcv() which will eventually return kOTNoDataErr, presenting
  857.         //    the application with a synchronization issue to be most careful about.
  858.         //    
  859.         //    In this application, since an OTRcv() calls are made from inside the notifier,
  860.         //    this particular synchronization issue doesn't become a problem.
  861.         //
  862.         case T_DATA:
  863.         {
  864.             //
  865.             //    Here we work around a small OpenTransport bug.
  866.             //    It turns out, since this program does almost everything from inside the notifier,
  867.             //    that during a T_UNBINDCOMPLETE we can put an EPInfo back into the idle list.
  868.             //    If that notification is interrupted by a T_LISTEN at the notifier, we could
  869.             //    end up starting a new connection on the endpoint before OT unwinds the stack
  870.             //    out of the code which delivered the T_UNBINDCOMPLETE.   OT has some specific
  871.             //    code to protect against a T_DATA arriving before the T_PASSCON, but in this
  872.             //    case it gets confused and the events arrive out of order.   If we try to
  873.             //    do an OTRcv() at this point we will get a kOTStateChangeErr because the endpoint
  874.             //    is still locked by the earlier OTAccept call until the T_PASSCON is delivered
  875.             //    to us.   This is fairly benign and can be worked around easily.  What we do
  876.             //    is note that the T_PASSCON hasn't arrived yet and defer the call to ReadData()
  877.             //    until it does.
  878.             //
  879.             if ( OTAtomicSetBit(&epi->stateFlags, kPassconBit) != 0 )
  880.             {
  881.                 //
  882.                 //    Because are are running completely inside notifiers,
  883.                 //    it is possible for a T_DATA to beat a T_PASSCON to us.
  884.                 //    We need to help OT out when this occurs and defer the
  885.                 //    data read until the T_PASSCON arrives.
  886.                 //
  887.                 ReadData(epi);
  888.             }
  889.             return;
  890.         }
  891.         
  892.         //
  893.         //    T_DISCONNECT:
  894.         //
  895.         //    An inbound T_DISCONNECT event usually indicates that the other side of the
  896.         //    connection did an abortive disconnect (as opposed to an orderly release).
  897.         //    It also can be generated by the transport provider on the system (e.g. tcp)
  898.         //    when it decides that a connection is no longer in existance.
  899.         //
  900.         //    We receive the disconnect, but this program ignores the associated reason (NULL param).
  901.         //    It is possible to get back a kOTNoDisconnectErr from the OTRcvDisconnect call.
  902.         //    This can happen when either (1) the disconnect on the stream is hidden by a 
  903.         //    higher priority message, or (2) something has flushed or reset the disconnect
  904.         //    event in the meantime.   This is not fatal, and the appropriate thing to do is
  905.         //    to pretend the T_DISCONNECT event never happened.   Any other error is unexpected
  906.         //    and needs to be reported so we can fix it.  Next, unbind the endpoint so we can
  907.         //    reuse it for a new inbound connection.
  908.         //    
  909.         //    It is possible to get an error on the unbind due to a bug in OT 1.1.1 and earlier.
  910.         //    The best thing to do for that is close the endpoint and open a new one to replace it.
  911.         //    We do this back in the main thread so we don't have to deal with synchronization problems.
  912.         //
  913.         case T_DISCONNECT:
  914.         {
  915.             DoRcvDisconnect(epi);
  916.             return;
  917.         }
  918.         
  919.         //
  920.         //    T_DISCONNECTCOMPLETE:
  921.         //
  922.         //    Sometimes this is called as a result of the 
  923.         //    I_FLUSH / OTSndDisconenct() combo in StopServer to relaim
  924.         //    all memory via T_MEMORYRELEASED events so we can close down.
  925.         //    We don't actually release any memory or remove the EPInfo
  926.         //    from a list so we don't have to synchronize with the main
  927.         //    thread.   It will get cleaned up on the next call to StopServer().
  928.         //
  929.         //    Note, this is where we would normally clear the stateFlags
  930.         //    for kFlushDisconnectInProgress, but since there is no point in
  931.         //    doing the flush/disconnect more than once, we never clear it.
  932.         //
  933.         //
  934.         case T_DISCONNECTCOMPLETE:
  935.         {
  936.             if (result != kOTNoError)
  937.                 DBAlert1("Notifier: T_DISCONNECT_COMPLETE result %d", result);
  938.             return;
  939.         }
  940.         
  941.         //
  942.         //    T_GODATA:
  943.         //
  944.         //    This event is received when flow control is lifted.   We are under flow control
  945.         //    whenever OTSnd() returns a kOTFlowErr or accepted less bytes than we attempted
  946.         //    to send.  Since SendData() is only called from inside the notifier, we don't
  947.         //    have to worry about interrupting another call to SendData() at this point.
  948.         //
  949.         //    Note, it is also possible to get a T_GODATA without having invoke flow control.
  950.         //    Be safe and prepare for this.
  951.         //
  952.         case T_GODATA:
  953.         {
  954.             SendData(epi);
  955.             return;
  956.         }
  957.         
  958.         //
  959.         //    T_LISTEN:
  960.         //
  961.         //    Call DoListenAccept() to do all the work.
  962.         //
  963.         case T_LISTEN:
  964.         {
  965.             DoListenAccept();
  966.             return;
  967.         }
  968.  
  969.         //
  970.         //    T_OPENCOMPLETE:
  971.         //
  972.         //    This event occurs when an OTAsyncOpenEndpoint() completes.   Note that this event,
  973.         //    just like any other async call made from outside the notifier, can occur during
  974.         //    the call to OTAsyncOpenEndpoint().  That is, in the main thread the program did
  975.         //    the OTAsyncOpenEndpoint(), and the notifier is invoked before control is returned
  976.         //    to the line of code following the call to OTAsyncOpenEndpoint().   This is one
  977.         //    event we need to keep track of even if we are shutting down the program since there
  978.         //    is no way to cancel outstanding OTAsyncOpenEndpoint() calls.
  979.         //
  980.         case T_OPENCOMPLETE:
  981.         {
  982.             TOptMgmt         optReq;
  983.             TOption            opt;
  984.                 
  985.             OTAtomicClearBit(&epi->stateFlags, kOpenInProgressBit);
  986.             if (result == kOTNoError)
  987.                 epi->erf = (EndpointRef) cookie;
  988.             else
  989.             {
  990.                 DBAlert1("Notifier: T_OPENCOMPLETE result %d", result);
  991.                 return;
  992.             }
  993.  
  994.             if (gProgramState != kProgramRunning)
  995.                 return;
  996.             
  997.             if (epi != gListener)
  998.                 gCntrEndpts++;
  999.             
  1000.             //
  1001.             //    Set to blocking mode so we don't have to deal with kEAGAIN errors.
  1002.             //    Async/blocking is the best mode to write an OpenTransport application in (imho).
  1003.             //
  1004.             err = OTSetBlocking(epi->erf);
  1005.             if (err != kOTNoError)
  1006.             {
  1007.                 DBAlert1("Notifier: T_OPENCOMPLETE - OTSetBlocking error %d", err);
  1008.                 return;
  1009.             }
  1010.             
  1011.             //
  1012.             //    Set to AckSends so OT doesn't slow down to copy data sent out.
  1013.             //    However, this requires special care when closing endpoints, so don't use
  1014.             //    AckSends unless you are prepared for this.   Never, ever, close an endpoint
  1015.             //    when a send has been done but the T_MEMORYRELEASED event hasn't been returned yet.
  1016.             //
  1017.             err = OTAckSends(epi->erf);
  1018.             if (err != kOTNoError)
  1019.             {
  1020.                 DBAlert1("Notifier: T_OPENCOMPLETE - OTAckSends error %d", err);
  1021.                 return;
  1022.             }
  1023.             
  1024.             //
  1025.             //    Option Management
  1026.             //
  1027.             //    Turn on ip_reuseaddr so we don't have port conflicts in general.
  1028.             //    We use local stack structures here since the memory for the 
  1029.             //    option request structure is free upon return.   If we were to request
  1030.             //    the option return value, we would have to use static memory for it.
  1031.             //
  1032.             optReq.flags            = T_NEGOTIATE;
  1033.             optReq.opt.len            = kOTFourByteOptionSize;
  1034.             optReq.opt.buf            = (unsigned char *) &opt;
  1035.             
  1036.             opt.len                    = sizeof(TOption);
  1037.             opt.level                = INET_IP;
  1038.             opt.name                = IP_REUSEADDR;
  1039.             opt.status                = 0;
  1040.             opt.value[0]            = 1;
  1041.             
  1042.             err = OTOptionManagement(epi->erf, &optReq, NULL);
  1043.             if (err != kOTNoError)
  1044.                 DBAlert1("Notifier: T_OPENCOMPLETE - OTOptionManagement err %d", err);
  1045.             
  1046.             //
  1047.             //    Code path resumes at T_OPTMGMTCOMPLETE
  1048.             //
  1049.             return;
  1050.         }
  1051.         
  1052.         //
  1053.         //    T_OPTMGMTCOMPLETE:
  1054.         //
  1055.         //    An OTOptionManagement() call has completed.  These are used on all
  1056.         //    endpoints to set IP_REUSEADDR.   It is also used for all endpoints
  1057.         //    other than the listener to set TCP_KEEPALIVE which helps recover
  1058.         //    server resources if the other side crashes or is unreachable.
  1059.         //
  1060.         case T_OPTMGMTCOMPLETE:
  1061.         {
  1062.             TBind                bindReq;
  1063.             InetAddress            inAddr;
  1064.             TOptMgmt             optReq;
  1065.             TKeepAliveOpt        opt;
  1066.             
  1067.             if (result != kOTNoError)
  1068.             {
  1069.                 DBAlert1("Notifier: T_OPTMGMTCOMPLETE result %d", result);
  1070.                 return;
  1071.             }
  1072.             
  1073.             if (epi != gListener)
  1074.             {
  1075.                 if ( OTAtomicSetBit(&epi->stateFlags, kIPReuseAddrBit) == 0 )
  1076.                 {
  1077.                     //
  1078.                     //    Turn on TCP_KEEPALIVE so we can recover from connections which have
  1079.                     //    gone away which we don't know about.   The keepalive value is set
  1080.                     //    very low here, probably too low for a real server.
  1081.                     //
  1082.                     optReq.flags            = T_NEGOTIATE;
  1083.                     optReq.opt.len            = sizeof(TKeepAliveOpt);
  1084.                     optReq.opt.buf            = (unsigned char *) &opt;
  1085.                     
  1086.                     opt.len                    = sizeof(TKeepAliveOpt);
  1087.                     opt.level                = INET_TCP;
  1088.                     opt.name                = TCP_KEEPALIVE;
  1089.                     opt.status                = 0;
  1090.                     opt.tcpKeepAliveOn        = 1;
  1091.                     opt.tcpKeepAliveTimer    = kTCPKeepAliveInSecs;    
  1092.                     
  1093.                     err = OTOptionManagement(epi->erf, &optReq, NULL);
  1094.                     if (err != kOTNoError)
  1095.                     {
  1096.                         DBAlert1("Notifier: T_OPTMGMTCOMPLETE - OTOptionManagement err %d", err);
  1097.                         return;
  1098.                     }
  1099.                 }
  1100.                 else
  1101.                 {
  1102.                     //
  1103.                     //    The endpoint now has both IP_REUSEADDR and TCP_KEEPALIVE set.
  1104.                     //    It is ready to go on the free list to accept an inbound connection.
  1105.                     //
  1106.                     OTLIFOEnqueue(gIdleEPs, &epi->link);
  1107.                     OTAtomicAdd32(1, &gCntrIdleEPs);
  1108.                     if (gListenPending)
  1109.                         EnterListenAccept();
  1110.                 }
  1111.                 return;
  1112.             }
  1113.             
  1114.             //
  1115.             //    Must be listener endpoint, do the bind.  Again, we use stack memory for 
  1116.             //    the bind request structure and NULL for the bind return structure.
  1117.             //
  1118.             inAddr.fAddressType     = AF_INET;
  1119.             inAddr.fPort            = gListenerPort;
  1120.             inAddr.fHost            = 0;                // allow inbound connections from any interface
  1121.             
  1122.             bindReq.addr.len         = sizeof(InetAddress);
  1123.             bindReq.addr.buf         = (unsigned char*) &inAddr;
  1124.             bindReq.qlen             = gListenerQueueDepth;
  1125.             
  1126.             err = OTBind(epi->erf, &bindReq, NULL);
  1127.             if (err != kOTNoError)
  1128.                 DBAlert1("Notifier: T_OPTMGMTCOMPLETE - OTBind error %d", err);
  1129.                 
  1130.             return;            // now wait for a T_LISTEN notification
  1131.         }
  1132.         
  1133.         //
  1134.         //    T_MEMORYRELEASED:
  1135.         //
  1136.         //    This event occurs when OpenTransport is done with the buffer passed in via
  1137.         //    an OTSnd() call with AckSends turned on.   The memory is free and we can reuse it.
  1138.         //    
  1139.         //    IMPORTANT NOTE:  This event is reentrant.   That is, this event will interrupt
  1140.         //    our notifier in progress, even interrupting a T_MEMORYRELEASED in progress, so
  1141.         //    it must be coded more carefully than most other events.
  1142.         //
  1143.         case T_MEMORYRELEASED:
  1144.         {
  1145.             OTAtomicAdd32(-1, &epi->outstandingSends);
  1146.             return;
  1147.         }
  1148.         
  1149.         //
  1150.         //    T_ORDREL:
  1151.         //
  1152.         //    This event occurs when an orderly release has been received on the stream.
  1153.         //
  1154.         case T_ORDREL:
  1155.         {
  1156.             err = OTRcvOrderlyDisconnect(epi->erf);
  1157.             if (err != kOTNoError)
  1158.             {
  1159.                 //
  1160.                 //    It is possible for several reasons for the T_ORDREL to have disappeared,
  1161.                 //    or be temporarily hidden, when we attempt the OTRcvOrderlyDisconnect().
  1162.                 //    The best thing to do when this happens is pretend that the event never
  1163.                 //    occured.   We will get another notification of T_ORDREL if the event
  1164.                 //    becomes unhidden later.  Any other form of error is unexpected and 
  1165.                 //    is reported back so we can correct it.
  1166.                 //
  1167.                 if (err == kOTNoReleaseErr)
  1168.                     return;
  1169.  
  1170.                 DBAlert1("Notifier: T_ORDREL - OTRcvOrderlyDisconnect error %d", err);
  1171.                 return;
  1172.             }
  1173.             
  1174.             //
  1175.             //    Sometimes our data sends get stopped with a kOTLookErr
  1176.             //    because of a T_ORDREL from the other side (which doesn't close
  1177.             //    the connection, it just means they are done sending data).
  1178.             //    If so, we still end up in the notifier with the T_ORDREL event,
  1179.             //    but we won't resume sending data unless we explictly check
  1180.             //    here whether or not we need to do so.
  1181.             //
  1182.             if (epi->sendBytes > 0)
  1183.             {
  1184.                 SendData(epi);
  1185.                 return;
  1186.             }
  1187.             
  1188.             //
  1189.             //    Check the endpoint state to see if we are in T_IDLE.  If so,
  1190.             //    the connection is fully broken down and we can unbind and requeue
  1191.             //    the endpoint for reuse.   If not, then wait until we have also done
  1192.             //    an OTSndOrderlyDisconnect, at which time we will also check the state of
  1193.             //    of the endpoint and unbind there if required.
  1194.             //
  1195.             epState = OTGetEndpointState(epi->erf);
  1196.             if (epState == T_IDLE)
  1197.                 CheckUnbind(epi, OTUnbind(epi->erf), kDontQueueIt);
  1198.                 
  1199.             return;
  1200.         }
  1201.         
  1202.         //
  1203.         //    T_PASSCON:
  1204.         //
  1205.         //    This event happens on the accepting endpoint, not the listening endpoint.
  1206.         //    At this point the connection is fully established and we can begin the
  1207.         //    process of downloading data.  Note that due to a problem in OT it is 
  1208.         //    possible for a T_DATA to beat a T_PASSCON to the notifier.  When this
  1209.         //    happens we note it in the T_DATA case and then start processing the 
  1210.         //    data here.  
  1211.         //
  1212.         case T_PASSCON:
  1213.         {
  1214.             if (result != kOTNoError)
  1215.             {
  1216.                 DBAlert1("Notifier: T_PASSCON result %d", result);
  1217.                 return;
  1218.             }    
  1219.             OTAtomicAdd32(1, &gCntrConnections);
  1220.             OTAtomicAdd32(1, &gCntrTotalConnections);
  1221.             OTAtomicAdd32(1, &gCntrIntervalConnects);
  1222.             if ( OTAtomicSetBit(&epi->stateFlags, kPassconBit) != 0 )
  1223.             {
  1224.                 //
  1225.                 //    A T_DATA previously beat the T_PASSCON to our notifier.
  1226.                 //    Here we help OT out by having deferred data processing until now.
  1227.                 //
  1228.                 ReadData(epi);
  1229.             }
  1230.             return;
  1231.         }
  1232.         
  1233.         //
  1234.         //    T_UNBINDCOMPLETE:
  1235.         //
  1236.         //    This event occurs on completion of an OTUnbind().
  1237.         //    The endpoint is ready for reuse on a new inbound connection.
  1238.         //    Put it back into the queue of idle endpoints.
  1239.         //    Note that the OTLIFO structure has atomic queue and dequeue,
  1240.         //    which can be helpful for synchronization protection.  
  1241.         //
  1242.         case T_UNBINDCOMPLETE:
  1243.         {
  1244.             CheckUnbind(epi, result, kQueueIt);
  1245.             return;
  1246.         }
  1247.         
  1248.         //
  1249.         //    default:
  1250.         //
  1251.         //    There are events which we don't handle, but we don't expect to see
  1252.         //    any of them.   When running in debugging mode while developing a program,
  1253.         //    we exit with an informational alert.   Later, in the production version
  1254.         //    of the program, we ignore the event and try to keep running.
  1255.         //
  1256.         default:
  1257.         {
  1258.             DBAlert1("Notifier: unknown event <%x>", event);
  1259.             return;
  1260.         }
  1261.     }
  1262. }
  1263.  
  1264. //
  1265. //    ReadData:
  1266. //
  1267. //    This routine attempts to read all available data from an endpoint.
  1268. //    Since this routine is only called from inside the notifier in the current
  1269. //    version of OTVirtualServer, it is not necessary to program to handle
  1270. //    getting back a T_DATA notification DURING an OTRcv() call, as would be
  1271. //    the case if we read from outside the notifier.   We must read until we
  1272. //    get a kOTNoDataErr in order to clear the T_DATA event so we will get
  1273. //    another notification of T_DATA in the future.
  1274. //
  1275. //    Currently this application uses no-copy receives to get data.  This obligates
  1276. //    the program to return the buffers to OT asap.  Since this program does nothing
  1277. //    with data other than count it, that's easy.  Future, more complex versions
  1278. //    of this program will do more interesting things with regards to that.
  1279. //
  1280. static void ReadData(EPInfo* epi)
  1281. {
  1282.     OTBuffer*    bp;
  1283.     OTResult    res;
  1284.     OTFlags        flags;
  1285.     OTResult    epState;
  1286.     Boolean        gotRequest = false;
  1287.     
  1288.     while (true)
  1289.     {
  1290.         res = OTRcv(epi->erf, &bp, kOTNetbufDataIsOTBufferStar, &flags);
  1291.         
  1292.         //
  1293.         //    Note, check for 0 because can get a real 0 length recive
  1294.         //    in some protocols (not in TCP), which is different from
  1295.         //    getting back a kOTNoDataErr.
  1296.         //
  1297.         if  (res >= 0 )
  1298.         {
  1299.             OTAtomicAdd32(res, &epi->rcvdBytes);
  1300.             OTAtomicAdd32(res, &gCntrIntervalBytes);
  1301.             OTReleaseBuffer(bp);
  1302.             if (epi->rcvdBytes >= kRequestSize)
  1303.             {
  1304.                 if (OTAtomicSetBit(&epi->stateFlags, kGotRequestBit) == 0)
  1305.                 {
  1306.                     //
  1307.                     //    We have gotten our 128 byte data request, so prepare to respond.
  1308.                     //    By setting the bit, we make sure that we can handle requests
  1309.                     //    which are bigger than expected without going weird.
  1310.                     //
  1311.                     epi->sendPtr    = gDataBuf;
  1312.                     epi->sendBytes    = gReturnDataLength;
  1313.                 }
  1314.             }
  1315.             continue;
  1316.         }
  1317.         
  1318.         if (res == kOTNoDataErr)
  1319.         {
  1320.             //
  1321.             //    Since ReadData is only called from inside the notifier we don't 
  1322.             //    have to worry about having missed a T_DATA during an OTRcv.
  1323.             //
  1324.             break;
  1325.         }
  1326.         
  1327.         if (res == kOTLookErr)
  1328.         {
  1329.             res = OTLook(epi->erf);
  1330.             if (res == T_ORDREL)
  1331.             {
  1332.                 //    
  1333.                 //    If we got the T_ORDREL, we won't get any more inbound data.
  1334.                 //    We return and wait for the notifier to get the T_ORDREL notification.
  1335.                 //    Upon getting it, we will notice we still need to send data and do so.
  1336.                 //    The T_ORDREL has to be cleared before we can send. 
  1337.                 //
  1338.                 return;
  1339.             }
  1340.             if (res == T_GODATA)
  1341.                 continue;
  1342.             
  1343.             DBAlert1("ReadData: OTRcv got OTLookErr 0x%08x", res);
  1344.         }
  1345.         else
  1346.         {
  1347.             epState = OTGetEndpointState(epi->erf);
  1348.             if (res == kOTOutStateErr && epState == T_INREL)
  1349.             {
  1350.                 //
  1351.                 //    Occasionally this problem will happen due to what appears
  1352.                 //    to be an OpenTransport notifier reentrancy problem.   
  1353.                 //    What has occured is that a T_ORDREL event happened and 
  1354.                 //    was processed during ReadData().   This is proven by being
  1355.                 //    in the T_INREL state without having done a call to
  1356.                 //    OTRcvOrderlyDisconnect() here.   It appears to be a benign 
  1357.                 //    situation, so the way to handle it is to understand that no
  1358.                 //    more data is going to arrive and go ahead and being our response
  1359.                 //    to the client.
  1360.                 //
  1361.                 break;
  1362.             }
  1363.             
  1364.             DBAlert2("ReadData: OTRcv error %d state %d", res, epState);
  1365.         }
  1366.         return;
  1367.     }
  1368.     
  1369.     SendData(epi);
  1370. }
  1371.  
  1372. //
  1373. //    Recycle:
  1374. //
  1375. //    This routine shouldn't be necessary, but it is helpful to work around both
  1376. //    problems in OpenTransport and bugs in this program.   Basically, whenever an
  1377. //    unexpected error occurs which shouldn't be fatal to the program, the EPInfo
  1378. //    is queued on the BrokenEP queue.  When recycle is called, once per pass around
  1379. //    the event loop, it will attempt to close the associated endpoint and open
  1380. //    a new one to replace it using the same EPInfo structure.   This process of
  1381. //    closing an errant endpoint and opening a replacement is probably the most
  1382. //    reliable way to make sure that this program and OpenTransport can recover
  1383. //    from unexpected happenings in a clean manner.
  1384. //
  1385. static void Recycle()
  1386. {
  1387.     OTLink*     list = OTLIFOStealList(gBrokenEPs);
  1388.     OTLink*        link;
  1389.     EPInfo*        epi;
  1390.  
  1391.     while ( (link = list) != NULL )
  1392.     {
  1393.         list = link->fNext;
  1394.         epi = OTGetLinkObject(link, EPInfo, link);
  1395.         if (!EPClose(epi))
  1396.         {
  1397.             OTLIFOEnqueue(gBrokenEPs, &epi->link);
  1398.             continue;
  1399.         }
  1400.         OTAtomicClearBit(&epi->stateFlags, kBrokenBit);
  1401.         OTAtomicAdd32(-1, &gCntrBrokenEPs);
  1402.         EPOpen(epi, OTCloneConfiguration(gCfgMaster));
  1403.     }
  1404. }
  1405.  
  1406. //
  1407. //    SendData:
  1408. //
  1409. //    For this first, simple version of the OT Virtual Server, we just send
  1410. //    a predefined number of bytes from a RAM buffer and then start an orderly
  1411. //    release sequence.  The assumption here is that we can send the entire buffer 
  1412. //    in one send.  Obviously future versions of this sample will have to do 
  1413. //    the OTSnd() in a more sophisticated way. 
  1414. //
  1415. static void SendData(EPInfo* epi)
  1416. {
  1417.     OTResult res;
  1418.     
  1419.     if (epi->sendBytes == 0)
  1420.         return;
  1421.         
  1422.     //
  1423.     //    Make sure we record that we are starting a send so we don't try to close
  1424.     //    the endpoint before a T_MEMORYRELEASED event is returned.
  1425.     //
  1426.     OTAtomicAdd32(1, &epi->outstandingSends);
  1427.     
  1428.     //
  1429.     //    In OT 1.1.2 and previous versions, there is a bug with AckSends
  1430.     //    which occurs when the same buffer is sent more than once.   In an attempt
  1431.     //    to go fast and not allocate memory, TCP may write an IP and TCP header
  1432.     //    into the data buffer which is sent.   If the buffer is sent more than once
  1433.     //    without being refreshed, the data may be corrupted.   To work around this,
  1434.     //    send the data via an OTData structure, using the gather-write mechanism.
  1435.     //    The problem does not occur in this code path, and this will not hinder performance.
  1436.     //    The problem will be fixed in the next Open Transport release following 1.1.2.
  1437.     //
  1438.     if (gOTVersion < kOTVersion113)
  1439.     {
  1440.         struct OTData data;
  1441.         
  1442.         data.fNext = NULL;
  1443.         data.fData = epi->sendPtr;
  1444.         data.fLen  = epi->sendBytes;
  1445.         res = OTSnd(epi->erf, &data, kNetbufDataIsOTData, 0);
  1446.     }
  1447.     else
  1448.     {
  1449.         res = OTSnd(epi->erf, epi->sendPtr, epi->sendBytes, 0);
  1450.     }
  1451.     if (res == gReturnDataLength)
  1452.     {
  1453.         //
  1454.         //    The entire buffer was accepted and we can begin the orderly release process.
  1455.         //
  1456.         OTAtomicAdd32(res, &gCntrTotalBytesSent);
  1457.         OTAtomicAdd32(res, &gCntrIntervalBytes);
  1458.         epi->sendPtr     = NULL;
  1459.         epi->sendBytes     = 0;
  1460.         
  1461.         if (gOTVersion < kOTVersion113)
  1462.         {
  1463.             //
  1464.             //    OT 1.1.2 and earlier versions have a bug in OT/TCP where 
  1465.             //    OT/TCP can lose an inbound orderly release if the orderly releases
  1466.             //    cross AND there is no time for the STREAMS service routines to fire.
  1467.             //    The workaround is to force the system back to system task time,
  1468.             //    and the event loop, before doing the orderly release.  This costs
  1469.             //    about 18% in connections/second performance in my testing, but
  1470.             //    the workaround is 100% reliable.   Here we set a stateFlag bit
  1471.             //    just in case the connection is disconnected while it is waiting
  1472.             //    for the orderly release to occur.
  1473.             //
  1474.             OTAtomicSetBit(&epi->stateFlags, kWaitingBit);
  1475.             OTLIFOEnqueue(gWaitingEPs, &epi->link);
  1476.         }
  1477.         else
  1478.             DoSndOrderlyDisconnect(epi);
  1479.             
  1480.         return;
  1481.     }
  1482.         
  1483.     if (res > 0)
  1484.     {
  1485.         //
  1486.         //    Implied kOTFlowErr since not all data was accepted.
  1487.         //    Currently SendData is only invoked from inside the notifier.
  1488.         //    If it was called from outside the notifier, it would need race
  1489.         //    protection against the T_GODATA happening before the OTSnd returned.
  1490.         //
  1491.         OTAtomicAdd32(res, &gCntrTotalBytesSent);
  1492.         OTAtomicAdd32(res, &gCntrIntervalBytes);
  1493.         epi->sendPtr     += res;
  1494.         epi->sendBytes     -= res;
  1495.     }
  1496.     
  1497.     else        // res =< 0
  1498.     {
  1499.         OTAtomicAdd32(-1, &epi->outstandingSends);
  1500.         if (res == kOTFlowErr)
  1501.             return;
  1502.         if (res == kOTLookErr)
  1503.         {
  1504.             res = OTLook(epi->erf);
  1505.             if (res == T_ORDREL)
  1506.             {
  1507.                 //
  1508.                 //    Wait to get the T_ORDREL at the notifier and handle it there.
  1509.                 //    Then we will resume sending.
  1510.                 //
  1511.                 return;
  1512.             }
  1513.             else
  1514.             {    
  1515.                 DBAlert1("SendData: OTSnd LOOK error %d", res);
  1516.             }
  1517.         }
  1518.         else
  1519.         {
  1520.             DBAlert1("SendData OTSnd error %d", res);
  1521.         }
  1522.     }
  1523. }
  1524.  
  1525. //
  1526. //    StartServer:
  1527. //
  1528. //    This routine gets memory for EPInfo structures.  It gets one for the listener 
  1529. //    endpoint and one for each of the acceptor endpoints.
  1530. //
  1531. static void StartServer()
  1532. {
  1533.     int        i;
  1534.     EPInfo* epi;
  1535.     size_t    bytes;
  1536.     
  1537.     gCntrEndpts                = 0;
  1538.     gCntrIdleEPs            = 0;
  1539.     gCntrTotalBrokenEPs        = 0;
  1540.     gCntrBrokenEPs            = 0;
  1541.     gCntrTotalBrokenEPs        = 0;
  1542.     gCntrTotalConnections    = 0;
  1543.     gCntrTotalBytesSent        = 0;
  1544.     gIdleEPs->fHead            = NULL;
  1545.     gBrokenEPs->fHead         = NULL;
  1546.     gWaitingEPs->fHead         = NULL;
  1547.     gServerState            = kServerRunning;
  1548.     
  1549.     //
  1550.     //    Save the current setting of max connections so we don't lose
  1551.     //    track of how much memory we will get if someone changes the
  1552.     //    dialog while the server is running.
  1553.     //
  1554.     gMaxConnectionsAllowed    = gMaxConnections;        
  1555.     
  1556.     //
  1557.     //    Get a block of memory to hold all the EPInfo structures.
  1558.     //    We use the first one for the listener.
  1559.     //    The rest are treated as an array of acceptors.
  1560.     //
  1561.     bytes = (gMaxConnectionsAllowed + 1) * sizeof(EPInfo);
  1562.     epi = (EPInfo*) NewPtr(bytes);
  1563.     if (epi == NULL)
  1564.     {
  1565.         DoAlert("Cannot get enough memory to allocate endpoints, exiting");
  1566.         return;
  1567.     }
  1568.     OTMemzero(epi, bytes);
  1569.     gListener    = epi++;
  1570.     gAcceptors    = epi;
  1571.     
  1572.     //
  1573.     //    Open listener, using the tilisten module to make 
  1574.     //    listen/accept/disconnect processing much simpler.
  1575.     //
  1576.     if (!EPOpen(gListener, OTCreateConfiguration("tilisten, tcp")))
  1577.         return;
  1578.             
  1579.     //
  1580.     //    Open endpoints to accept inbound connections.
  1581.     //    Note that any configuration passed in to OTOpenEndpoint is destroyed,
  1582.     //    so we create a master configuration, clone it once for each connection,
  1583.     //    which saves a lot of OT processing, and then destroy the master 
  1584.     //    configuration at the end.
  1585.     //
  1586.     gCfgMaster = OTCreateConfiguration("tcp");
  1587.     if (gCfgMaster == NULL)
  1588.     {
  1589.         DBAlert("StartServer: OTCreateConfiguration returned NULL");
  1590.         return;
  1591.     }
  1592.     for (epi = gAcceptors, i = 0; i < gMaxConnectionsAllowed; epi++, i++)
  1593.     {
  1594.         if (!EPOpen(epi, OTCloneConfiguration(gCfgMaster)))
  1595.             break;
  1596.     }
  1597. }
  1598.  
  1599. //
  1600. //    StopServer:
  1601. //
  1602. //    This is where the server is shut down, either because the user clicked
  1603. //    the stop button, or because the program is exiting (error or quit).
  1604. //    The two tricky parts are (1) we can't quit while there are outstanding
  1605. //    OTAsyncOpenEndpoint calls (which can't be cancelled, by the way), and
  1606. //    (2) we can't close endpoints until that have received all expected
  1607. //    T_MEMORYRELEASED events.
  1608. //
  1609. static void StopServer()
  1610. {
  1611.     int            i;
  1612.     EPInfo        *epi;
  1613.     Boolean        allClosed = true;
  1614.     
  1615.     gServerState = kServerShuttingDown;
  1616.     
  1617.     //
  1618.     //    Since the LIFOs shouldn't be used any longer, we clear them here.
  1619.     //
  1620.     (void) OTLIFOStealList(gBrokenEPs);
  1621.     (void) OTLIFOStealList(gIdleEPs);
  1622.     (void) OTLIFOStealList(gWaitingEPs);
  1623.     
  1624.     //
  1625.     //    Attempt to close all endpoints.
  1626.     //    EPClose doesn't mind being called again with epi->erf == NULL.
  1627.     //
  1628.     for (epi = gListener, i = 0; i < (gMaxConnectionsAllowed + 1); epi++, i++)
  1629.     {
  1630.         if (!EPClose(epi))
  1631.             allClosed = false;
  1632.     }
  1633.     
  1634.     //
  1635.     //    If we successfully deleted all of the endpoints, we can release
  1636.     //    the memory and head home for Christmas now...
  1637.     //
  1638.     if (allClosed)
  1639.     {
  1640.         DisposPtr((char*)gListener);
  1641.         OTDestroyConfiguration(gCfgMaster);
  1642.         gListener             = NULL;
  1643.         gAcceptors             = NULL;
  1644.         gCntrIdleEPs        = 0;
  1645.         gCntrBrokenEPs        = 0;
  1646.         gCntrConnections    = 0;
  1647.         gServerState         = kServerStopped;
  1648.     }
  1649. }
  1650.  
  1651. //
  1652. //    TimerInit
  1653. //
  1654. //    Start up a regular timer to do housekeeping.   Strictly speaking,
  1655. //    this isn't necessary, but having a regular heartbeat allows us to
  1656. //    detect if we are so busy with network notifier processing that the
  1657. //    program's event loop isn't ever firing.   We want to know this so
  1658. //    we can at least allow the user to quit the program if they want to.
  1659. //
  1660. static void TimerInit()
  1661. {
  1662.     gTimerTask = OTCreateTimerTask(&TimerRun, 0);
  1663.     if (gTimerTask == 0)
  1664.     {
  1665.         DBAlert("TimerInit: OTCreateTimerTask returned 0");
  1666.         return;
  1667.     }
  1668.     OTScheduleTimerTask(gTimerTask, kTimerInterval);
  1669. }
  1670.  
  1671. //
  1672. //    TimerDestroy
  1673. //
  1674. static void TimerDestroy()
  1675. {
  1676.     if (gTimerTask != 0)
  1677.     {
  1678.         OTCancelTimerTask(gTimerTask);
  1679.         OTDestroyTimerTask(gTimerTask);
  1680.         gTimerTask = 0;
  1681.     }
  1682. }
  1683.  
  1684. //
  1685. //    TimerRun
  1686. //
  1687. //    Fires every N seconds, no matter how busy the system is.
  1688. //    We use this to detect if the program's main event loop is getting no time,
  1689. //    in which case we can slow the server down by doing a throttle-back until
  1690. //    the event loop can run at least once.  It also is a convenient statistics 
  1691. //    gathering point.  
  1692. //
  1693. static pascal void TimerRun(void*)
  1694. {
  1695.     gConnectsPerSecond = (gCntrIntervalConnects / kTimerIntervalInSeconds);
  1696.     gKBytesPerSecond = (gCntrIntervalBytes / (kTimerIntervalInSeconds * 1024));
  1697.     gEventsPerSecond = (gCntrIntervalEventLoop / kTimerIntervalInSeconds);
  1698.     if (gCntrIntervalEventLoop == 0)
  1699.         gWaitForEventLoop = true;
  1700.     
  1701.     if (gEventsPerSecond > gEventsPerSecondMax)
  1702.         gEventsPerSecondMax = gEventsPerSecond;
  1703.         
  1704.     if (gAllowNewMax == 0)
  1705.     {
  1706.         //
  1707.         //    Avoid bytes/second data skewing from early buffering by not allowing 
  1708.         //    the first non-zero measurement to be saved as a max.  We could use an 
  1709.         //    exponential weighted average instead, but since our timer doesn't fire 
  1710.         //    very often, the stats take too long to become valid that way.
  1711.         //
  1712.         if (gConnectsPerSecond > gConnectsPerSecondMax)
  1713.             gConnectsPerSecondMax = gConnectsPerSecond;
  1714.         if (gKBytesPerSecond > gKBytesPerSecondMax)
  1715.             gKBytesPerSecondMax = gKBytesPerSecond;
  1716.     }
  1717.     
  1718.     if (gConnectsPerSecond > 0)
  1719.     {
  1720.         if (gAllowNewMax > 0)
  1721.             gAllowNewMax--;
  1722.     }
  1723.     else
  1724.         gAllowNewMax = kTimerHitsBeforeAcceptMax;
  1725.  
  1726.     gCntrIntervalConnects    = 0;
  1727.     gCntrIntervalBytes        = 0;
  1728.     gCntrIntervalEventLoop    = 0;
  1729.     gDoWindowUpdate            = true;
  1730.     gCntrConnections        = gCntrEndpts - gCntrIdleEPs - gCntrBrokenEPs;
  1731.     
  1732.     OTScheduleTimerTask(gTimerTask, kTimerInterval);
  1733. }
  1734.  
  1735.  
  1736. //////////////////////////////////////////////////////////////////////////////////////
  1737. //
  1738. //    Macintosh Program Wrapper
  1739. //
  1740. //    The code from here down deals with the Macintosh environment, events,
  1741. //    menus, command keys, etc.   Networking code is in the section above.
  1742. //    Since this code is fairly basic, and since this isn't really intended
  1743. //    to be a "sample Macintosh application" (just a sample OpenTransport application)
  1744. //    this section isn't heavily commented.   There are much better Macintosh
  1745. //    application samples for handling mouse, keyboard, event loops, etc.
  1746. //
  1747. //////////////////////////////////////////////////////////////////////////////////////
  1748.  
  1749. static void AboutBox()
  1750. {
  1751.     Alert(kAboutBoxResID, NULL);
  1752. }
  1753.  
  1754. static Boolean EventDialog(EventRecord* event)
  1755. {
  1756.     DialogPtr     dp;
  1757.     short        item;
  1758.     short        itemType;
  1759.     Handle        itemHandle;
  1760.     Rect        itemRect;
  1761.     
  1762.     if (event->modifiers & cmdKey)
  1763.     {
  1764.         EventKeyDown(event);        // this allows menu commands while dialog is active window
  1765.         return false;                // note if I add cut/paste I will have to rework this.
  1766.     }
  1767.     if ((DialogSelect(event, &dp, &item)) && (dp == gDialogPtr))
  1768.     {
  1769.         GetDItem(gDialogPtr, item, &itemType, &itemHandle, &itemRect);
  1770.         switch (item)
  1771.         {
  1772.             case kListenerPortDItem:
  1773.                 GetIText(itemHandle, gListenerPortStr);
  1774.                 return true;
  1775.             
  1776.             case kListenerQueueDepthDItem:
  1777.                 GetIText(itemHandle, gListenerQueueDepthStr);
  1778.                 return true;
  1779.             
  1780.             case kMaxConnectionsDItem:
  1781.                 GetIText(itemHandle, gMaxConnectionsStr);
  1782.                 return true;
  1783.             
  1784.             case kReturnDataLengthDItem:
  1785.                 GetIText(itemHandle, gReturnDataLengthStr);
  1786.                 return true;
  1787.                 
  1788.             case kStartStopDItem:
  1789.                 GetDItem(gDialogPtr, kStartStopDItem, &itemType, &itemHandle, &itemRect);
  1790.                 if (gServerRunning)
  1791.                 {
  1792.                     StopServer();                
  1793.                     SetCTitle((ControlHandle)itemHandle, gStartStr);
  1794.                     gServerRunning = false;
  1795.                 }
  1796.                 else
  1797.                 {
  1798.                     TCPPrefsReset();
  1799.                     StartServer();
  1800.                     SetCTitle((ControlHandle)itemHandle, gStopStr);
  1801.                     gServerRunning = true;
  1802.                 }
  1803.                 DrawDialog(gDialogPtr);
  1804.                 return true;
  1805.         }
  1806.     }
  1807.     return false;
  1808. }
  1809.  
  1810. static void TCPPrefsReset()
  1811. {
  1812.     StringToNum(gListenerPortStr, &gListenerPort);
  1813.     StringToNum(gListenerQueueDepthStr, &gListenerQueueDepth);
  1814.     StringToNum(gMaxConnectionsStr, &gMaxConnections);
  1815.     StringToNum(gReturnDataLengthStr, &gReturnDataLength);
  1816.     if (gReturnDataLength > kDataBufSize)
  1817.         gReturnDataLength = kDataBufSize;
  1818. }
  1819.  
  1820. static void TCPPrefsDialog()
  1821. {
  1822.     short    itemType;
  1823.     Handle    itemHandle;
  1824.     Rect    itemRect;
  1825.     
  1826.     gDialogPtr = GetNewDialog(kTCPPrefsDlogResID, NULL, kInFront);
  1827.     SetWTitle(gDialogPtr, "\pTCP Preferences");
  1828.     GetDItem(gDialogPtr, kListenerPortDItem, &itemType, &itemHandle, &itemRect);
  1829.     SetIText(itemHandle, gListenerPortStr);
  1830.     GetDItem(gDialogPtr, kListenerQueueDepthDItem, &itemType, &itemHandle, &itemRect);
  1831.     SetIText(itemHandle, gListenerQueueDepthStr);
  1832.     GetDItem(gDialogPtr, kMaxConnectionsDItem, &itemType, &itemHandle, &itemRect);
  1833.     SetIText(itemHandle, gMaxConnectionsStr);
  1834.     GetDItem(gDialogPtr, kReturnDataLengthDItem, &itemType, &itemHandle, &itemRect);
  1835.     SetIText(itemHandle, gReturnDataLengthStr);
  1836.     GetDItem(gDialogPtr, kStartStopDItem, &itemType, &itemHandle, &itemRect);
  1837.     if (gServerRunning)
  1838.         SetCTitle((ControlHandle)itemHandle, gStopStr);
  1839.     else
  1840.         SetCTitle((ControlHandle)itemHandle, gStartStr);
  1841.     DrawDialog(gDialogPtr);
  1842. }
  1843.  
  1844. static void DialogClose()
  1845. {
  1846.     DisposDialog(gDialogPtr);
  1847.     gDialogPtr = NULL;
  1848.     TCPPrefsReset();
  1849. }
  1850.  
  1851. static void MenuDispatch(long menu)
  1852. {
  1853.     short menuID;
  1854.     short cmdID;
  1855.     
  1856.     menuID = HiWord(menu);
  1857.     cmdID  = LoWord(menu);
  1858.     switch(menuID)
  1859.     {
  1860.         case kAppleMenuResID:
  1861.         {
  1862.             switch (cmdID)
  1863.             {
  1864.                 case kAppleMenuAbout:
  1865.                     AboutBox();
  1866.                     break;
  1867.                     
  1868.                 default:
  1869.                     break;
  1870.             }
  1871.             break;
  1872.         }
  1873.             
  1874.         case kFileMenuResID:
  1875.         {
  1876.             switch (cmdID)
  1877.             {
  1878.                 case kFileMenuQuit:
  1879.                     gProgramState = kProgramDone;
  1880.                     break;
  1881.                     
  1882.                 case kFileMenuOpen:
  1883.                     WindowOpen();
  1884.                     break;
  1885.                     
  1886.                 case kFileMenuClose:
  1887.                     WindowClose();
  1888.                     break;
  1889.                     
  1890.                 default:
  1891.                     break;
  1892.             }
  1893.             break;
  1894.         }
  1895.         
  1896.         case kEditMenuResID:
  1897.             break;
  1898.         
  1899.         case kServerMenuResID:
  1900.         {
  1901.             switch (cmdID)
  1902.             {
  1903.                 case kServerMenuTCPPrefs:
  1904.                     TCPPrefsDialog();
  1905.                     break;
  1906.                     
  1907.                 default:
  1908.                     break;
  1909.             }
  1910.             break;
  1911.         }
  1912.     }
  1913.         
  1914. }
  1915.  
  1916. static void EventDrag(WindowPtr wp, Point loc)
  1917. {
  1918.     Rect dragBounds;
  1919.     
  1920.     dragBounds = qd.screenBits.bounds;
  1921.     DragWindow(wp, loc, &dragBounds);
  1922. }
  1923.  
  1924. static void EventGoAway(WindowPtr wp, Point loc)
  1925. {
  1926.     if (TrackGoAway(wp, loc))
  1927.     {
  1928.         if (wp == gWindowPtr)
  1929.             WindowClose();
  1930.         else if (wp == gDialogPtr)
  1931.             DialogClose();
  1932.     }
  1933. }
  1934.     
  1935. static void EventMouseDown(EventRecord* event)
  1936. {
  1937.     short        part;
  1938.     WindowPtr    wp;
  1939.     long         menu;
  1940.     
  1941.     part = FindWindow(event->where, &wp);
  1942.     switch (part)
  1943.     {
  1944.         case inMenuBar:
  1945.             menu = MenuSelect(event->where);
  1946.             HiliteMenu(0);
  1947.             MenuDispatch(menu);
  1948.             break;
  1949.             
  1950.         case inDrag:
  1951.             EventDrag(wp, event->where);
  1952.             break;
  1953.         
  1954.         case inGoAway:
  1955.             EventGoAway(wp, event->where);
  1956.             break;
  1957.         
  1958.         case inContent:        
  1959.             SelectWindow(wp);
  1960.             break;
  1961.             
  1962.         case inGrow:        // no grow box
  1963.         case inZoomIn:        // no zoom box
  1964.         case inZoomOut:        // no zoom box
  1965.         case inSysWindow:
  1966.         case inDesk:
  1967.         default:
  1968.             break;
  1969.     }
  1970. }
  1971.  
  1972. static void EventKeyDown(EventRecord* event)
  1973. {
  1974.     char        c;
  1975.     long        menu;
  1976.     
  1977.     c = event->message & charCodeMask;
  1978.     if (event->modifiers & cmdKey)
  1979.     {
  1980.         // cmd key
  1981.         menu = MenuKey(c);
  1982.         HiliteMenu(0);
  1983.         if (menu != 0)
  1984.             MenuDispatch(menu);
  1985.     }
  1986.     else
  1987.     {
  1988.         // normal keystroke
  1989.     }
  1990. }
  1991.  
  1992. static void EventLoop()
  1993. {
  1994.     EventRecord event;
  1995.     
  1996.     while ((gProgramState == kProgramRunning) || (gServerState != kServerStopped))
  1997.     {
  1998.         OTAtomicAdd32(1, &gCntrIntervalEventLoop);
  1999.         if (WaitNextEvent(everyEvent, &event, gSleepTicks, 0)) 
  2000.         {
  2001.             if ((gDialogPtr != NULL) && (IsDialogEvent(&event)))
  2002.             {
  2003.                 if (EventDialog(&event))
  2004.                     continue;
  2005.             }
  2006.             switch (event.what)
  2007.             {
  2008.                 case keyDown:
  2009.                     EventKeyDown(&event);
  2010.                     break;
  2011.                     
  2012.                 case mouseDown:
  2013.                     EventMouseDown(&event);
  2014.                     break;
  2015.                     
  2016.                 case updateEvt:
  2017.                     // redraw window now
  2018.                     break;
  2019.                 
  2020.                 case activateEvt:
  2021.                     // activate or deactivate window controls
  2022.                     break;
  2023.                 
  2024.                 case mouseUp:
  2025.                 case keyUp:
  2026.                 case autoKey:
  2027.                 case diskEvt:
  2028.                 case app4Evt:
  2029.                 default:
  2030.                     break;
  2031.             }
  2032.         }
  2033.         
  2034.         if ((gProgramState == kProgramRunning) && (gServerState == kServerRunning))
  2035.         {
  2036.             NetEventLoop();
  2037.         }
  2038.         else if (((gProgramState == kProgramRunning) && (gServerState == kServerShuttingDown)) ||
  2039.                  ((gProgramState != kProgramRunning) && (gServerState != kServerStopped)))
  2040.         {
  2041.             StopServer();
  2042.         }
  2043.         WindowUpdate();
  2044.     }
  2045. }
  2046.  
  2047. static void WindowClose()
  2048. {
  2049.     if (gWindowPtr == NULL)
  2050.         return;
  2051.     DisposeWindow(gWindowPtr);
  2052.     gWindowPtr = NULL;
  2053. }
  2054.  
  2055. static void WindowOpen()
  2056. {
  2057.     if (gWindowPtr != NULL)
  2058.         return;
  2059.     gWindowPtr = GetNewWindow(kWindowResID, NULL, kInFront);
  2060.     SetWTitle(gWindowPtr, "\pOTVirtualServer");
  2061. }
  2062.  
  2063. static void WindowUpdate()
  2064. {
  2065.     char gStrBuf[128];
  2066.     int len;
  2067.     
  2068.     if (gWindowPtr == NULL)
  2069.         return;
  2070.         
  2071.     if (gDoWindowUpdate == false)
  2072.         return;
  2073.     gDoWindowUpdate = false;
  2074.         
  2075.     gCntrConnections = gCntrEndpts - gCntrIdleEPs - gCntrBrokenEPs;
  2076.     
  2077.     SetPort(gWindowPtr);
  2078.     EraseRgn(gWindowPtr->visRgn);
  2079.     
  2080.     MoveTo(20, 20);
  2081.     sprintf(gStrBuf, "EPs: total %d idle %d", gCntrEndpts, gCntrIdleEPs);
  2082.     len = strlen(gStrBuf) ;
  2083.     DrawText(gStrBuf, 0, len);
  2084.     
  2085.     MoveTo(20, 40);
  2086.     sprintf(gStrBuf, "Connects: current %d total %d", gCntrConnections, gCntrTotalConnections);
  2087.     len = strlen(gStrBuf) ;
  2088.     DrawText(gStrBuf, 0, len);
  2089.  
  2090.     MoveTo(20, 60);
  2091.     sprintf(gStrBuf, "KBytes sent %d", (gCntrTotalBytesSent / 1024));
  2092.     len = strlen(gStrBuf) ;
  2093.     DrawText(gStrBuf, 0, len);
  2094.         
  2095.     MoveTo(20, 80);
  2096.     sprintf(gStrBuf, "Conn/sec: current %d max %d", gConnectsPerSecond, gConnectsPerSecondMax);
  2097.     len = strlen(gStrBuf) ;
  2098.     DrawText(gStrBuf, 0, len);
  2099.     
  2100.     MoveTo(20, 100);
  2101.     sprintf(gStrBuf, "KBy/sec: current %d max %d", gKBytesPerSecond, gKBytesPerSecondMax);
  2102.     len = strlen(gStrBuf) ;
  2103.     DrawText(gStrBuf, 0, len);
  2104.     
  2105.     MoveTo(20, 120);
  2106.     sprintf(gStrBuf, "Events/sec: %d/%d", gEventsPerSecond, gEventsPerSecondMax);
  2107.     len = strlen(gStrBuf) ;
  2108.     DrawText(gStrBuf, 0, len);
  2109.     
  2110.     MoveTo(20, 140);
  2111.     sprintf(gStrBuf, "Running at %d%% of capacity.", 
  2112.             (100 - ((100 * gEventsPerSecond)/gEventsPerSecondMax)));
  2113.     len = strlen(gStrBuf) ;
  2114.     DrawText(gStrBuf, 0, len);
  2115.     
  2116.     MoveTo(20, 160);
  2117.     sprintf(gStrBuf, "Broken EPs: %d total: %d.", gCntrBrokenEPs, gCntrTotalBrokenEPs);
  2118.     len = strlen(gStrBuf) ;
  2119.     DrawText(gStrBuf, 0, len);
  2120.  
  2121.     MoveTo(20, 180);
  2122.     sprintf(gStrBuf, "OTVersion 0x%08x", gOTVersion);
  2123.     len = strlen(gStrBuf) ;
  2124.     DrawText(gStrBuf, 0, len);
  2125.  
  2126. }
  2127.  
  2128. static void SetupMenus()
  2129. {
  2130.     MenuHandle mh;
  2131.     mh = GetMenu(kAppleMenuResID);
  2132.     AddResMenu( mh, 'DRVR' );            /* Add DA list */
  2133.     InsertMenu(mh, 0);
  2134.     mh = GetMenu(kFileMenuResID);
  2135.     InsertMenu(mh, 0);
  2136.     mh = GetMenu(kEditMenuResID);
  2137.     InsertMenu(mh, 0);
  2138.     mh = GetMenu(kServerMenuResID);
  2139.     InsertMenu(mh, 0);
  2140.     DrawMenuBar();
  2141. }
  2142.  
  2143. static void C2PStr(char* cstr, Str255 pstr)
  2144. {
  2145.     //
  2146.     //    Converts a C string to a Pascal string.
  2147.     //    Truncates the string if longer than 254 bytes.
  2148.     //
  2149.     int i, j;
  2150.     
  2151.     i = strlen(cstr);
  2152.     if (i > 254)
  2153.         i = 254;
  2154.     pstr[0] = i;
  2155.     for (j = 1; j <= i; j++)
  2156.         pstr[j] = cstr[j-1];
  2157. }
  2158.  
  2159. static void P2CStr(Str255 pstr, char* cstr)
  2160. {
  2161.     int i;
  2162.     
  2163.     for (i = 0; i < pstr[0]; i++)
  2164.         cstr[i] = pstr[i+1];
  2165.     cstr[i] = 0;
  2166. }
  2167.  
  2168. static void AlertExit(char* err)
  2169. {
  2170.     Str255 pErr;
  2171.     
  2172.     C2PStr(err, pErr);
  2173.     ParamText(pErr, NULL, NULL, NULL);
  2174.     Alert(kAlertExitResID, NULL);
  2175.     ExitToShell();    
  2176. }
  2177.  
  2178. static void MacInitROM()
  2179. {
  2180.     MaxApplZone();
  2181.     MoreMasters();
  2182.     InitGraf(&qd.thePort);
  2183.     InitCursor();
  2184.     InitFonts();
  2185.     InitWindows();
  2186.     InitMenus();
  2187.     TEInit();
  2188.     InitDialogs(NULL);
  2189.     FlushEvents(everyEvent, 0);
  2190. }
  2191.  
  2192. static void MacInit()
  2193. {
  2194.     MacInitROM();
  2195.     WindowOpen();
  2196.     SetupMenus();
  2197. }
  2198.  
  2199. static void MiscInit()
  2200. {
  2201.     //    Initialize the temporary data buffer so it isn't all zeros.
  2202.  
  2203.     int i;
  2204.     unsigned char x = 0;
  2205.     
  2206.     for (i = 0; i < kDataBufSize; i++)
  2207.         gDataBuf[i] = x++;
  2208. }
  2209.  
  2210. void main()
  2211. {
  2212.     MacInit();
  2213.     NetInit();
  2214.     MiscInit();
  2215.     EventLoop();
  2216.     NetShutdown();
  2217.     if (gProgramState == kProgramError)
  2218.         AlertExit(gProgramErr);
  2219. }
  2220.